package xyz.hzxiao.utils;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * 分片上传快速接入
 *
 * @author ：FuXx
 * @className : xyz.hzxiao.utils.ShardingUpload
 * @since ：2023/3/7
 */
public class ShardingUpload {

    public static ShardingUploadBuilder builder(File file) throws IOException {
        return new ShardingUploadBuilder(file);
    }

    public static class ShardingUploadBuilder {
        /**
         * 分片上传阈值，每个分片的大小，用于计算文件有多少个分片。单位为字节，每个分片1 MB。
         */
        private static final long PART_SIZE = 1 * 1024 * 1024;

        private static final String TEMP_DIR = "E:\\temp\\sharding\\";

        private static final String URL = "http://localhost:1999/file/shardingUpload";

        private static final String UPLOAD_SUCCESS = "http://localhost:1999/file/uploadSuccess";

        /**
         * 要上传的文件
         */
        private final File file;

        /**
         * 任务ID
         */
        private final String taskId;

        /**
         * 文件名
         */
        private final String fileName;

        /**
         * 当前文件的MD5,不是分片的
         */
        private final String md5;

        /**
         * 分片文件
         *
         * @throws IOException 文件异常
         * @serialData 2023年3月3日
         * @author ：FuXx
         */
        public ShardingUploadBuilder onSharding() throws IOException {
            long fileLength = this.file.length();
            // 分片总数
            int partCount = (int) (fileLength / PART_SIZE);
            if (fileLength % PART_SIZE != 0) {
                partCount++;
            }
            try (RandomAccessFile rafRead = new RandomAccessFile(this.file, "r")) {
                byte[] buff = new byte[1024];
                for (int i = 0; i < partCount; i++) {
                    File shardingFile = new File(TEMP_DIR + i);
                    RandomAccessFile rafWrite = new RandomAccessFile(shardingFile, "rw");
                    int len;
                    while ((len = rafRead.read(buff)) != -1) {
                        rafWrite.write(buff, 0, len);
                        if (shardingFile.length() == PART_SIZE) {
                            break;
                        }
                    }
                    rafWrite.close();
                }
            }
            return this;
        }

        /**
         * 上传分片文件
         *
         * @return 当前上传对象
         * @throws IOException 文件异常
         */
        public ShardingUploadBuilder onUpload() throws IOException {
            File file = new File(TEMP_DIR);
            File[] files = Objects.requireNonNull(file.listFiles());
            List<File> fileList = Arrays.asList(files);
            fileList.sort(Comparator.comparingInt(o -> Integer.parseInt(o.getName())));
            int length = fileList.size();
            for (int i = 0; i < length; i++) {
                File shardingFile = fileList.get(i);
                String response = this.shardingUpload(length, i, shardingFile.length(), shardingFile);
                if (response.contains("true")) {
                    shardingFile.delete();
                }
            }
            return this;
        }

        /**
         * 上传完成
         *
         * @throws IOException 文件异常
         */
        public String onSuccess() throws IOException {
            // 上传完成
            Map<String, Object> param = new HashMap<>(2);
            param.put("sourceFileName", this.fileName);
            return NetRequest.builder().postJson(UPLOAD_SUCCESS, param).responseAtString();
        }

        /**
         * 分片上传文件
         *
         * @param partCount   分片总数
         * @param startPos    当前的分片数
         * @param curPartSize 当前分片大小
         * @param file        当前分片文件
         * @serialData 2023年3月3日
         * @author ：FuXx
         */
        private String shardingUpload(int partCount, long startPos, long curPartSize, File file) throws IOException {
            RequestBody requestBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", this.fileName, RequestBody.create(MediaType.parse("video/mp4"), file))
                    .addFormDataPart("id", this.taskId)
                    .addFormDataPart("chunks", String.valueOf(partCount))
                    .addFormDataPart("chunk", String.valueOf(startPos))
                    .addFormDataPart("size", String.valueOf(curPartSize))
                    .addFormDataPart("name", this.fileName)
                    .addFormDataPart("md5", this.md5).build();
            return NetRequest.builder().uploadFile(URL, requestBody).responseAtString();
        }

        public ShardingUploadBuilder(File file) throws IOException {
            this.file = file;
            this.taskId = UUID.randomUUID().toString();
            this.fileName = file.getName();
            this.md5 = DigestUtils.md5Hex(Files.readAllBytes(file.toPath()));
        }
    }

    public static void main(String[] args) throws IOException {
        File file = new File("D:\\图片\\1080P.mp4");
        ShardingUpload.builder(file)
                .onSharding()
                .onUpload()
//                .onSuccess()
        ;
    }
}
